EP4. 최신 LLM 구조 1편 — 의미 표현력과 계산 효율성
현대 LLM은 토큰 임베딩 차원 확장으로 의미 표현력을 높이는 동시에, MoE·Flash Attention·KV Cache·GQA로 계산·메모리 효율을 확보하는 두 축의 공학적 균형 위에 설계된다.
1. 주제 정의
최신 LLM 구조의 발전 방향은 의미 표현력(semantic expressiveness)과 계산 효율성(computational efficiency)을 동시에 향상시키는 것입니다. 단순 파라미터 수 증가가 아니라 구조적 분기(MoE·GQA·KVQ)를 통해 두 목표를 달성합니다.
핵심 아이디어: 토큰 임베딩 차원을 최우선으로 늘리되, FFN·어텐션 연산은 선택적 활성화와 캐시 재사용으로 비용을 억제한다.
2. 풀려는 문제
- 문제 1 — 의미 표현력 한계: dense 임베딩 차원이 작으면 토큰이 구분할 수 있는 의미 축이 부족해 미묘한 문맥·관계를 정확히 인코딩하지 못한다.
- 문제 2 — FFN 계산량 폭증: 임베딩 차원을 늘리면 FFN(Feed-Forward Network) 연산량이 비례 이상 증가한다. 차원이 10,000을 넘으면 전체 차원에 dense FFN을 적용하는 것은 GPU 메모리·FLOPS 양쪽에서 불가능하다.
- 문제 3 — 어텐션 메모리 병목: KV 연산은 시퀀스 길이에 비례해 HBM(High Bandwidth Memory) 접근이 반복되고, 토큰 수가 늘어날수록 KV 재계산 비용이 선형 누적된다.
- 문제 4 — Q 헤드 중복 계산: MHA(Multi-Head Attention)는 모든 토큰마다 Q·K·V를 독립 계산한다. Q 헤드 수만큼 KV 캐시도 헤드별로 유지되어 메모리를 과다 점유한다.
실무 노하우: 위 4개 문제는 독립적이지 않습니다. 임베딩 차원을 늘리면 문제 1은 해결되지만 문제 2·3·4가 동시에 악화됩니다. 현대 아키텍처는 이 연쇄를 끊기 위해 아래 5개 기법을 조합합니다.
3. 핵심 개념·구조
-
토큰 임베딩 차원 확장 (
hidden_size): 임베딩 차원 = 토큰이 표현할 수 있는 의미 축의 수. 어휘 크기(vocab_size) × hidden_size 만큼 임베딩 파라미터가 증가하므로, 같은 파라미터 예산 내에서 KVQ 가중치·FFN 가중치보다 임베딩 차원 확장이 의미 표현력 향상 대비 비용이 가장 효율적이다. 임베딩 차원이 커야 어텐션 헤드·레이어 수도 늘릴 수 있다. -
MoE — Mixture of Experts: FFN 레이어를 N개의 전문가(expert FFN)로 분할하고, 라우터(router)가 각 토큰 벡터를 분석해 일부 전문가만 활성화한다. 전체 파라미터 수는 많아도 실제 연산에 참여하는 파라미터(활성 파라미터)는 제한된다. 라우터 학습 자체도 역전파로 최적화된다. 임베딩 차원이 4,000을 초과하면 dense FFN은 GPU에서 감당 불가하므로 MoE는 사실상 필수다.
-
Flash Attention: GPU 메모리는 고속 SRAM(on-chip)과 대용량 HBM(off-chip) 두 계층으로 구성된다. 기존 어텐션은 어텐션 행렬을 HBM에 쓰고 다시 읽는 왕복 비용이 크다. Flash Attention은 어텐션 계산을 타일(tile) 단위로 SRAM 안에서 완결해 HBM 왕복을 최소화한다. 메모리를 적게 쓰는 것이 아니라 속도를 높이는 기법이다.
-
KV Cache + KV Quantization: 자기회귀 생성(autoregressive generation) 시 이전 토큰의 K·V는 수학적으로 동일하므로 HBM에 캐시로 저장해 재계산을 피한다. Q는 현재 토큰마다 다르므로 캐시 불가. KV 양자화는 이 캐시를 fp16 대신 fp8·int4로 압축 저장해 HBM 점유를 줄인다.
-
GQA — Grouped Query Attention: MHA의 발전형. KV에는 입력 토큰의 영향을 그대로 반영하되, Q 헤드는 그룹별 고정 값(학습된 파라미터)을 사용한다. 여러 Q 헤드가 소수의 KV 헤드 그룹을 공유하여 KV 캐시 크기를 헤드 수 비율만큼 절감한다.
[어텐션 구조 비교]
MHA (Multi-Head Attention):
Q1 K1 V1
Q2 K2 V2 <- 모든 헤드가 독립 KV 보유 -> KV 캐시 = 헤드 수 x 시퀀스 길이
Q3 K3 V3
GQA (Grouped Query Attention):
Q1 -+
Q2 -+- K1 V1 <- Q 헤드 그룹 3개가 KV 헤드 1개 공유
Q3 -+
Q4 -+
Q5 -+- K2 V2 -> KV 캐시 = (헤드 수 / 그룹 크기) x 시퀀스 길이
Q6 -+
[MoE 라우터 흐름]
입력 토큰 임베딩
|
[Router] <- 학습된 라우팅 가중치
/ | \
Expert1 Expert2 Expert3 (N개 중 k개만 활성화)
\ | /
출력 결합 (weighted sum)
4. 구현 가이드 (Do It Yourself)
시작 전 (Before you begin)
이 섹션을 완료하면 Anthropic API 토큰 카운팅, Prompt Caching 적용, HuggingFace 모델 config 검사, vLLM KV 캐시 양자화를 실제 코드로 검증할 수 있습니다.
선수 조건:
- Python 3.10+
- pip install anthropic (또는 transformers, vllm)
- 환경 변수 ANTHROPIC_API_KEY 설정
소요 시간: 약 20분.
Step 1 — Anthropic Claude 호출 + 토큰 카운팅 (임베딩 차원·context window 비용 체감)
목표: 모델별 토큰 사용량을 측정해 임베딩 차원·context window의 비용 영향을 체감합니다.
다음 코드를 step1_token_count.py에 저장합니다.
import anthropic
client = anthropic.Anthropic() # ANTHROPIC_API_KEY 환경 변수 자동 사용
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=[
{"role": "user", "content": "GQA와 MHA의 KV 캐시 크기 차이를 1문단으로 설명해라."}
],
)
print("input_tokens :", response.usage.input_tokens)
print("output_tokens:", response.usage.output_tokens)
print("text :", response.content[0].text)
이 코드는 Anthropic Messages API로 단일 요청을 보내고 usage.input_tokens와 output_tokens를 출력합니다.
주의:
model인자에는 SSOT 승인 모델만 사용하세요. 본 가이드 작성 시점(2026-05 기준) 승인된 모델:claude-sonnet-4-6,claude-haiku-4-5-20251001,claude-opus-4-7.실무 노하우: 토큰당 비용은 모델 등급에 비례합니다. 단순 분류·라우팅은
claude-haiku-4-5-20251001로 시작하고, 깊은 추론만claude-opus-4-7로 escalate하는 비용 최적화 패턴이 일반적입니다.참고: Anthropic Python SDK — Messages API (2026-05 기준).
확인: usage.input_tokens가 양수로 출력되면 Step 1 완료.
Step 2 — Prompt Caching 적용 (API 레벨 KV Cache — 시스템 프롬프트 재사용)
목표: 큰 시스템 프롬프트에 prompt caching을 적용해 입력 토큰 비용을 절감합니다.
import anthropic
client = anthropic.Anthropic()
LONG_SYSTEM = "LLM 구조 학습 컨텍스트. " * 500 # 대용량 시스템 프롬프트 시뮬레이션
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=512,
system=[{"type": "text", "text": LONG_SYSTEM, "cache_control": {"type": "ephemeral"}}],
messages=[{"role": "user", "content": "MoE 라우터의 역할을 1문장으로 요약해라."}],
)
print("cache_read :", response.usage.cache_read_input_tokens)
print("cache_create :", response.usage.cache_creation_input_tokens)
cache_control 블록이 5분 TTL의 prompt cache를 활성화합니다.
주의: cache TTL은 5분. 5분 이상 휴면 후 재호출하면 cache miss로 비용 절감이 없습니다.
실무 노하우: LLM 내부의 KV Cache와 API의 Prompt Caching은 다른 레이어입니다. 전자는 토큰 간 K·V 재사용(모델 내부), 후자는 시스템 프롬프트 재계산 회피(API 레벨). 원리는 "이미 계산한 representation 재활용"으로 동일합니다.
참고: Anthropic Prompt Caching docs (2026-05 기준).
확인: 두 번째 호출부터 cache_read_input_tokens > 0이면 Step 2 완료.
Step 3 — HuggingFace Transformers로 GQA config 직접 검사
목표: 로컬 모델의 임베딩 차원·KV 헤드 수를 직접 확인해 강의 개념을 코드로 체감합니다.
from transformers import AutoConfig
config = AutoConfig.from_pretrained("Qwen/Qwen2.5-7B")
print("hidden_size (임베딩 차원):", config.hidden_size)
print("num_attention_heads (Q 헤드):", config.num_attention_heads)
print("num_key_value_heads (KV 헤드):", config.num_key_value_heads)
print("GQA 그룹 크기:", config.num_attention_heads // config.num_key_value_heads)
num_attention_heads / num_key_value_heads 비율이 GQA 그룹 크기입니다.
실무 노하우:
num_key_value_heads < num_attention_heads이면 GQA 적용 모델. Llama-3.1-8B는 32/8=4, Qwen2.5-7B는 28/4=7. KV 캐시 절감 비율은 그룹 크기와 비례합니다.
확인: num_key_value_heads < num_attention_heads이면 Step 3 완료.
Step 4 — vLLM으로 KV Cache 양자화 서빙
목표: 강의의 "KV 캐시 양자화"를 실제 서빙 엔진에서 적용합니다.
from vllm import LLM, SamplingParams
llm = LLM(model="Qwen/Qwen2.5-7B-Instruct", kv_cache_dtype="fp8")
outputs = llm.generate(["GQA의 핵심을 1문장으로."], SamplingParams(max_tokens=64))
print(outputs[0].outputs[0].text)
kv_cache_dtype="fp8"이 강의의 "KV 캐시 양자화"를 직접 구현합니다.
주의: fp8은 H100·A100(Hopper·Ampere) 등 일부 GPU만 지원합니다. 미지원 GPU에서는 fp16/bf16 기본값을 유지하세요.
참고: vLLM 공식 docs + PagedAttention 논문 arxiv:2309.06180 (2026-05 기준).
확인: nvidia-smi로 KV 캐시 메모리 점유가 감소하면 Step 4 완료.
Step 마지막 — 동작 최종 확인
import anthropic
client = anthropic.Anthropic()
r = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=200,
messages=[{"role": "user", "content": "MoE 라우터 학습 방식을 1문단으로 설명해라."}],
)
print(r.content[0].text)
예상 출력: MoE 라우터·전문가 선택 메커니즘에 대한 일관된 한국어 설명. usage 필드 토큰 수가 양수.
5. 적용 사례 (공신력 오픈소스)
- Anthropic Python SDK (
https://github.com/anthropics/anthropic-sdk-python) — Messages API·Streaming·Prompt Caching 패턴. Step 1·2가 직접 활용. Apache 2.0. - HuggingFace Transformers (
https://github.com/huggingface/transformers) — Llama·Qwen·Mistral 등 GQA 적용 모델의 config 직접 검사(Step 3).num_key_value_heads가 GQA 그룹 크기를 노출. Apache 2.0. - vLLM (
https://github.com/vllm-project/vllm) — PagedAttention +kv_cache_dtype="fp8"KV 캐시 양자화(Step 4). Apache 2.0. GitHub stars 40K+(2026-05 기준). - Ollama (
https://github.com/ollama/ollama) — 로컬에서 GQA·MoE 모델(Mixtral·Qwen-MoE 등) 즉시 실행.ollama run mixtral:8x7b한 줄로 MoE 체감 가능. MIT. - LangChain (
https://github.com/langchain-ai/langchain) — Anthropic·OpenAI·HuggingFace LLM 통합 + Prompt Caching 래핑. LCEL chain 내 모델 swap 패턴. MIT. - LiteLLM (
https://github.com/BerriAI/litellm) — 100+ vendor 통합 proxy + 토큰·비용 추적. MoE 모델 가격 차이(Mixtral 8x22B vs dense 70B) 측정에 유용. MIT.
실무 노하우: Tier 1 벤더(Anthropic·OpenAI) 직접 SDK가 가장 안정적입니다. 래핑 라이브러리는 추상화 누수·버전 충돌 가능성이 있으므로, multi-vendor switch·prompt template 관리 같은 명확한 가치가 있을 때만 도입하세요.
6. 핵심 원리
- 의미 표현력 향상 =
hidden_size확장 -> 토큰이 표현할 수 있는 의미 축 수 증가 -> 미묘한 의미·문맥·관계 구분 능력 향상. - 계산 효율성 향상 = 세 전략의 조합:
- 선택적 활성화: MoE로 FFN 연산 참여 부분을 제한.
- 재사용: KV Cache(모델 내부), Prompt Cache(API 레벨)로 이미 계산한 representation 재활용.
- 메모리 접근 최적화: Flash Attention으로 HBM 왕복 최소화, GQA·KV 양자화로 캐시 점유 절감.
- 두 축의 trade-off: 단순 dense scaling은 의미 표현력과 계산 효율성을 동시에 악화시킨다. 구조적 분기(MoE·GQA·KVQ)가 이 trade-off를 해소하는 핵심 수단이다.
7. 변형·확장
- MoE 변형: dense -> MoE(Mixtral 8x7B), shared expert(DeepSeek-V2), fine-grained expert(Qwen2-MoE).
- Attention 변형: MHA -> GQA -> MQA(Multi-Query Attention, KV 헤드 1개) -> MLA(Multi-head Latent Attention, DeepSeek-V2).
- KV Cache 변형: fp16 -> fp8 -> int4 양자화, RingAttention(다중 GPU 분산), PagedAttention(vLLM, 메모리 단편화 제거).
- 임베딩 차원 변형: dense embedding -> factorized embedding(ALBERT) -> RoPE(Rotary Position Embedding, 위치 정보 통합).
8. 다른 도구·접근과의 비교 (3-way)
| 비교 축 | Dense Transformer (GPT-2 스타일) | MoE Transformer (Mixtral 8x7B 스타일) | SSM / Mamba |
|---|---|---|---|
| FFN 활성 파라미터 | 전체 (총 = 활성) | 일부 (총 >> 활성) | N/A (FFN 없음) |
| 어텐션 구조 | MHA | GQA 또는 MHA | 선형 SSM (어텐션 없음) |
| KV 캐시 필요 여부 | 필요 | 필요 (GQA로 축소) | 불필요 (상태 벡터) |
| 장문맥 효율 | O(n^2) 비용 | O(n^2) 비용, GQA로 경감 | O(n) 선형 |
| 표현력 (2026-05 기준) | 높음 | 높음 (활성 파라미터 이상) | 일부 task에서 MHA 이하 |
참고: Hybrid 아키텍처(Jamba = SSM + Transformer)는 KV cache를 일부 레이어에만 사용해 메모리·표현력의 균형을 추구합니다.
9. 한계·트레이드오프
- MoE 한계: 라우터 불안정(expert collapse — 특정 전문가에 편중), fine-tuning 시 expert drift, 분산 서빙 시 전문가 간 통신 비용 증가.
- GQA 한계: KV 헤드 그룹화로 일부 task(특히 long-context needle retrieval)에서 MHA 대비 미세 성능 저하 가능.
- KV Cache 양자화 한계: fp8은 Hopper·Ampere GPU에서만 지원. int4 양자화는 긴 생성 시 품질 손실 가능성.
- Flash Attention 한계: SRAM 크기 제약으로 극단적으로 긴 시퀀스(수백만 토큰)에서는 타일 분할 오버헤드 발생.
- Prompt Cache 한계: TTL 5분(Anthropic 기준, 2026-05), 시스템 프롬프트 일부 변경 시 cache miss -> 비용 절감 없음.
10. 최신 권장 패턴 (2026-05 기준)
| 시나리오 | 권장 구성 |
|---|---|
| 비용 최적화 추론 | claude-haiku-4-5-20251001 + Prompt Caching |
| 깊은 추론 (코드·수학) | claude-sonnet-4-6 또는 claude-opus-4-7 + Extended Thinking |
| 로컬 서빙 (1~2 GPU, Ampere 이상) | Qwen2.5-7B-Instruct + vLLM (kv_cache_dtype="fp8") |
| Long context (>100K 토큰) | claude-sonnet-4-6 + Prompt Caching + streaming |
| 다중 vendor switching | LiteLLM proxy + fallback chain |
| 로컬 MoE 체감 | ollama run mixtral:8x7b |
참고: Anthropic 모델 문서 (2026-05 기준).
11. 메타인지 자기평가
Step 1 — 현재 시스템 점검
# 현재 코드베이스의 모델 식별자 확인
grep -rn "model=" --include="*.py" | grep -E "(claude|gpt|llama|qwen)"
Step 2 — 적용 가능성 평가 체크리스트
- [ ] 시스템 프롬프트가 1K 토큰 이상인가? -> Prompt Caching 후보.
- [ ] 같은 분류·라우팅 작업에 Sonnet·Opus를 사용 중인가? ->
claude-haiku-4-5-20251001로 다운그레이드 후보. - [ ] 로컬 GPU가 H100·A100(Ampere 이상)인가? ->
kv_cache_dtype="fp8"후보. - [ ] Long context (>32K) 요청이 빈번한가? -> GQA 적용 모델(Llama-3·Qwen2.5) 우선.
- [ ] 현재 모델의
num_key_value_heads를 확인한 적 있는가? -> Step 3 코드로 즉시 확인 가능.
Step 3 — 점진 적용 순서
- 가장 빈번한 API call 1개에 Prompt Caching 적용.
cache_read_input_tokens로그로 cache hit rate 측정. - 분류·라우팅 call 1개를
claude-haiku-4-5-20251001로 swap. accuracy regression 1주일 모니터링. - 로컬 서빙이면 vLLM으로 마이그레이션.
kv_cache_dtype변경 전후nvidia-smi메모리 비교. - MoE 모델(Mixtral·Qwen2-MoE) 도입 검토 시 LiteLLM으로 비용 차이 먼저 측정.
클릭하거나 Space를 눌러 뒤집기